package com.zjw.socket.demo.starter.utils;


import com.zjw.socket.demo.starter.annotation.NonNull;
import com.zjw.socket.demo.starter.annotation.Nullable;
import com.zjw.socket.demo.starter.core.Reflector;

import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

public final class JarResourcesHolder {

  private static final String TAG = "JarResourcesHolder";

  private static final String JAR_MATCH = "jar!";

  private static final String PROTOCOL_FILE = "file:";

  private static final String CLASS_SUFFIX = ".class";
  private static final String CLASS_INNER = "$";

  @Nullable
  public static String getJarPath(@NonNull URL url) {
    if (url == null) {
      return null;
    }
    String file = url.getFile();

    int jarIndex = file.indexOf(JAR_MATCH);
    if (jarIndex == -1) {
      return null;
    }
    int jarStart = 0;
    if (file.startsWith(PROTOCOL_FILE)) {
      jarStart = PROTOCOL_FILE.length();
    }

    LogUtils.debug(TAG, "getJarPath " + file);
    return file.substring(jarStart, jarIndex + JAR_MATCH.length() - 1);
  }


  @NonNull
  public static List<Class<?>> resolveClassesFromJarFile(String jarPath, String rootPath, @Nullable Class<?> clazz) {
    List<Class<?>> classes = resolveClassesFromJarFile(jarPath, rootPath);
    List<Class<?>> result = new ArrayList<>();
    for (Class<?> aClass : classes) {
      if (clazz == null || Reflector.isSuperClassOrInterfaceImpl(aClass, clazz)) {
        result.add(aClass);
      }
    }
    return result;
  }

  @NonNull
  @SuppressWarnings("unchecked")
  public static <T> List<T> resolveClassesFromJarFileAndInstance(String jarPath, String rootPath, Class<T> clazz) {
    List<Class<?>> classes = resolveClassesFromJarFile(jarPath, rootPath, clazz);
    List<T> result = new ArrayList<>();

    for (Class<?> aClass : classes) {
      Object o = Reflector.instanceEmptyConstructor(aClass);
      if (o == null) {
        continue;
      }
      result.add((T) o);
    }
    return result;
  }

  @NonNull
  public static List<Class<?>> resolveClassesFromJarByAnnotation(String jarPath, String rootPath, Class<? extends Annotation> clazz) {
    List<Class<?>> classes = resolveClassesFromJarFile(jarPath, rootPath);
    return classes.stream().filter(it -> it.isAnnotationPresent(clazz)).collect(Collectors.toList());
  }


  @NonNull
  public static List<Object> resolveClassesFromJarByAnnotationAndInstance(String jarPath, String rootPath, Class<? extends Annotation> clazz) {
    List<Class<?>> classes = resolveClassesFromJarByAnnotation(jarPath, rootPath, clazz);
    List<Object> result = new ArrayList<>();
    for (Class<?> aClass : classes) {
      Object o = Reflector.instanceEmptyConstructor(aClass);
      if (o != null) {
        result.add(o);
      }
    }
    return result;
  }

  /**
   * 从jar文件中读取指定目录下面的所有的class文件
   *
   * @return 所有的的class的对象
   */
  public static List<Class<?>> resolveClassesFromJarFile(String jarPath, String rootPath) {
    List<Class<?>> classes = new ArrayList<>();

    rootPath = rootPath.replace(".", "/");
    if (!rootPath.endsWith("/")) {
      rootPath += "/";
    }

    try (JarFile jarFile = new JarFile(jarPath)) {

      for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements(); ) {
        JarEntry jarEntry = entries.nextElement();
        String name = jarEntry.getName();
        if (!name.endsWith(CLASS_SUFFIX)) {
          continue;
        }
        if (!name.startsWith(rootPath)) {
          continue;
        }
        if (name.contains(CLASS_INNER)) {
          continue;
        }

        String className = name.replace("/", ".").substring(0, name.length() - CLASS_SUFFIX.length());
        Class<?> aClass = Reflector.resolveClass(className);
        classes.add(aClass);
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    return classes;
  }

}
